<!DOCTYPE html>
<!--
Copyright (c) 2013 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/core/test_utils.html">
<link rel="import" href="/tracing/extras/chrome_config.html">
<link rel="import" href="/tracing/model/model.html">

<script>
'use strict';

tr.b.unittest.testSuite(function() {
  const Process = tr.model.Process;
  const Thread = tr.model.Thread;
  const AsyncSlice = tr.model.AsyncSlice;
  const AsyncSliceGroup = tr.model.AsyncSliceGroup;
  const newAsyncSlice = tr.c.TestUtils.newAsyncSlice;
  const newAsyncSliceEx = tr.c.TestUtils.newAsyncSliceEx;
  const newModel = tr.c.TestUtils.newModel;

  test('asyncSliceGroupBounds_Empty', function() {
    const thread = {};
    const g = new AsyncSliceGroup(thread);
    g.updateBounds();
    assert.isTrue(g.bounds.isEmpty);
  });

  test('asyncSliceGroupBounds_Basic', function() {
    const model = new tr.Model();
    const p1 = new Process(model, 1);
    const t1 = p1.getOrCreateThread(456);
    const g = new AsyncSliceGroup(t1);
    g.push(newAsyncSlice(0, 1, t1, t1));
    g.push(newAsyncSlice(1, 1.5, t1, t1));
    assert.strictEqual(g.length, 2);
    g.updateBounds();
    assert.strictEqual(g.bounds.min, 0);
    assert.strictEqual(g.bounds.max, 2.5);
  });

  test('asyncSliceGroupChildEvents', function() {
    const t1 = {};  // Fake thread.
    const g = new AsyncSliceGroup(t1);
    const sl1 = newAsyncSlice(0, 1, t1, t1);
    const sl2 = newAsyncSlice(1, 3, t1, t1);
    const sl2sub1 = newAsyncSlice(1, 2, t1, t1);
    sl2.subSlices.push(sl2sub1);
    const sl2sub1sub1 = newAsyncSlice(2, 1, t1, t1);
    sl2sub1.subSlices.push(sl2sub1sub1);
    const sl2sub2 = newAsyncSlice(3, 1, t1, t1);
    sl2.subSlices.push(sl2sub2);
    g.push(sl1);
    g.push(sl2);

    assert.sameMembers(
        Array.from(g.childEvents()), [sl1, sl2, sl2sub1, sl2sub1sub1, sl2sub2]);
  });

  test('asyncSliceGroupShiftTimestamps', function() {
    const t1 = {};  // Fake thread.
    const g = new AsyncSliceGroup(t1);
    const sl1 = newAsyncSlice(1, 2, t1, t1);
    const sl2 = newAsyncSlice(3, 4, t1, t1);
    const sl2sub1 = newAsyncSlice(3.5, 2, t1, t1);
    sl2.subSlices.push(sl2sub1);
    const sl2sub1sub1 = newAsyncSlice(4, 0.5, t1, t1);
    sl2sub1.subSlices.push(sl2sub1sub1);
    g.push(sl1);
    g.push(sl2);

    g.updateBounds();
    assert.strictEqual(g.bounds.min, 1);
    assert.strictEqual(g.bounds.max, 7);

    g.shiftTimestampsForward(1.5);
    g.updateBounds();
    assert.strictEqual(g.bounds.min, 2.5);
    assert.strictEqual(g.bounds.max, 8.5);
    assert.strictEqual(sl2sub1.start, 5);
    assert.strictEqual(sl2sub1.duration, 2);
    assert.strictEqual(sl2sub1sub1.start, 5.5);
    assert.strictEqual(sl2sub1sub1.duration, 0.5);
  });

  test('asyncSliceGroupViewSubGroups', function() {
    const model = new tr.Model();
    const p1 = new Process(model, 1);
    p1.name = 'Renderer';
    const t1 = p1.getOrCreateThread(321);
    t1.name = 'MainThread';
    const g = new AsyncSliceGroup(t1);
    g.push(newAsyncSliceEx(
        { title: 'VeryBusy',
          start: 0, duration: 1 }));
    g.push(newAsyncSliceEx(
        { cat: 'renderer.scheduler',
          id: ':ptr:0xdeadbeef', title: 'FrameScheduler.Foo',
          start: 0.5, duration: 0.1 }));
    g.push(newAsyncSliceEx(
        { cat: 'renderer.scheduler',
          id: ':ptr:0xdeadbeef', title: 'FrameScheduler.Bar',
          start: 0.55, duration: 0.2 }));
    g.push(newAsyncSliceEx(
        { cat: 'renderer.scheduler',
          id: ':ptr:0x1ee7beef', title: 'FrameScheduler.Baz',
          start: 0.3, duration: 0.3 }));
    g.push(newAsyncSliceEx(
        { cat: 'renderer.scheduler',
          id: ':ptr:0x1ee7beef', title: 'FrameScheduler.Baz',
          start: 0.7, duration: 0.2 }));
    g.push(newAsyncSliceEx(
        { title: 'VeryBusy',
          start: 1, duration: 1.5 }));
    g.push(newAsyncSliceEx(
        { title: 'Loading',
          start: 0, duration: 5 }));
    assert.strictEqual(g.length, 7);

    const vsg = g.viewSubGroups;
    assert.strictEqual(vsg.length, 4);
    // Groups must be sorted by title.
    assert.strictEqual(vsg[0].title, 'Frame:ptr:0x1ee7beef');
    assert.strictEqual(vsg[1].title, 'Frame:ptr:0xdeadbeef');
    assert.strictEqual(vsg[2].title, 'Loading');
    assert.strictEqual(vsg[3].title, 'VeryBusy');
    // Check nested view sub-groups.
    assert.strictEqual(vsg[2].viewSubGroups.length, 0);
    assert.strictEqual(vsg[3].viewSubGroups.length, 0);
    const wf1vsg = vsg[0].viewSubGroups;
    assert.strictEqual(wf1vsg.length, 1);
    assert.strictEqual(wf1vsg[0].title, 'Baz');
    assert.strictEqual(wf1vsg[0].getSettingsKey(),
        'processes.Renderer.MainThread.Frame:ptr:0x1ee7beef.Baz');
    const wf2vsg = vsg[1].viewSubGroups;
    assert.strictEqual(wf2vsg.length, 2);
    assert.strictEqual(wf2vsg[0].title, 'Bar');
    assert.strictEqual(wf2vsg[0].getSettingsKey(),
        'processes.Renderer.MainThread.Frame:ptr:0xdeadbeef.Bar');
    assert.strictEqual(wf2vsg[1].title, 'Foo');
    assert.strictEqual(wf2vsg[1].getSettingsKey(),
        'processes.Renderer.MainThread.Frame:ptr:0xdeadbeef.Foo');
  });

  test('asyncSliceGroupStableId', function() {
    const model = new tr.Model();
    const process = model.getOrCreateProcess(123);
    const thread = process.getOrCreateThread(456);
    const group = new AsyncSliceGroup(thread);

    assert.strictEqual(process.stableId, 123);
    assert.strictEqual(thread.stableId, '123.456');
    assert.strictEqual(group.stableId, '123.456.AsyncSliceGroup');
  });

  test('asyncSliceParentContainerSetAtPush', function() {
    const m = newModel(function(m) {
      m.process = m.getOrCreateProcess(123);
      m.thread = m.process.getOrCreateThread(456);
      m.group = new AsyncSliceGroup(m.thread);

      m.sA = m.group.push(newAsyncSliceEx(
          { title: 'sA', start: 0.0, duration: 10.0 }));
    });

    assert.deepEqual(m.sA.parentContainer, m.thread);
  });
});
</script>
